package cn.arrow.note.validation.common.exception;


import cn.arrow.note.validation.common.enums.ResultCodeEnum;
import cn.arrow.note.validation.common.vo.ResultObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import javax.validation.UnexpectedTypeException;
import java.util.Objects;

/**
 * 全局异常捕获处理
 *
 * @author Walker
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    /**
     * 方法参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultObject<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("[全局异常处理] [参数校验不通过]{}", e.getMessage(), e);
        return ResultObject.<String>builder()
                .success(false)
                .code(ResultCodeEnum.USER_PARAMETER_ERROR.getCode())
                .msg(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage())
                .build();
    }

    /**
     * 方法RequestParam/PathVariable形式参数校验异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResultObject<String> handleConstraintViolationException(ConstraintViolationException e) {
        log.error("[全局异常处理] [参数校验不通过]{}", e.getMessage(), e);
        return ResultObject.<String>builder()
                .success(false)
                .code(ResultCodeEnum.USER_PARAMETER_ERROR.getCode())
                .msg(e.getMessage())
                .build();
    }

    /**
     * 方法参数校验异常[类型不配备]
     */
    @ExceptionHandler(UnexpectedTypeException.class)
    public ResultObject<String> handleUnexpectedTypeException(UnexpectedTypeException e) {
        log.error("[全局异常处理] [参数校验类型不匹配]{}", e.getMessage(), e);
        return ResultObject.builder(e.getMessage()).success(false)
                .code(ResultCodeEnum.USER_PARAMETER_UNEXPECTED_TYPE.getCode())
                .msg("校验参数类型异常，请检查注释使用是否正确")
                .build();
    }

    /**
     * 自定义异常
     */
    @ExceptionHandler(GlobalException.class)
    public ResultObject<String> handleGlobalException(HttpServletResponse response, GlobalException e) {

        if (ResultCodeEnum.USER_NOT_PERMISSION.getCode().equals(e.getCode())) {
            // 未授权
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
        }
        log.error("[全局异常处理] [自定义异常]{}", e.getMessage(), e);
        return ResultObject.builder(e.getMessage()).success(false)
                .code(e.getCode())
                .msg(e.getMessage())
                .build();
    }

    /**
     * 捕捉其他所有异常
     */
    @ExceptionHandler(Exception.class)
    public Object handleOtherException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);

        if (status == HttpStatus.NOT_FOUND) {
            log.error("[全局异常处理] [ajax请求] 用户访问页面不存在，异常信息", ex);
            return ResultObject.builder()
                    .code(ResultCodeEnum.SERVICE_INTERFACE_NOT_FOUND.getCode())
                    .msg(ResultCodeEnum.SERVICE_INTERFACE_NOT_FOUND.getMsg())
                    .success(false)
                    .data(null)
                    .build();
        }

        if (status == HttpStatus.BAD_REQUEST) {
            log.error("[全局异常处理] [ajax请求] 用户访问路径错误不存在，异常信息", ex);
            return ResultObject.builder()
                    .code(ResultCodeEnum.SERVICE_INTERFACE_NOT_FOUND.getCode())
                    .msg(ResultCodeEnum.SERVICE_INTERFACE_NOT_FOUND.getMsg())
                    .success(false)
                    .data(null)
                    .build();
        }

        // 系统异常
        log.error("[全局异常处理] [ajax请求] 出现异常，异常信息", ex);
        return ResultObject.builder()
                .code(ResultCodeEnum.SYSTEM_ERROR.getCode())
                .msg(ResultCodeEnum.SYSTEM_ERROR.getMsg())
                .success(false)
                .data(null)
                .build();

    }


    /**
     * 获取响应状态码
     *
     * @param request 请求
     * @return Http状态码
     */
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }
}